;=================================================
; sample database
;=================================================

(load "utility/cmd_interpreter.lsp")

;=================================================
; global database
;
(defvar *db* nil)

;=================================================
; simple print
(defun println (text)
 (format t "~a~%" text)
 )

;=================================================
; \func make-cd
; returns structured list which contains an appropriate
; cd-description record
(defun make-cd (title artist rating ripped)
 ; validation checks are suppresed

 ; return structured list
 (list :title title :artist artist :rating rating :ripped ripped)
 )

;=================================================
; filling given data base
;
(defun add-record (cd)
 (push cd *db*)
 )

;=================================================
; fill with fake data
;
(defun fake-fill-db ()
 (defun add (title artist rating ripped) (add-record (make-cd title artist rating ripped)))

 (add "Just push play" "Aerosmith" 10 t)
 (add "A momentary lapse of reason" "Pink Floyd" 15 t)
 (add "Somewhere in time" "Iron Maiden" 9 nil)
 (add "Bounce" "Bon Jovi" 11 t)
 )

;=================================================
; dumps contents
;
(defun dump-contents (db-contents)
 ; iterate through each cd record
 (dolist (cd db-contents)
  (format t "~{~a:~10t~a~%~}~%" cd)
  )
 )

;=================================================
; dump database' contents
;
(defun dump-db ()
 ; dump wtf is that
 (println "dump-db:")
 (dump-contents *db*)
 )

;=================================================
; get user input
;
(defun prompt-read (prompt)
 (format *query-io* "~a: " prompt)
 (force-output *query-io*)
 (read-line *query-io*)
 )

;=================================================
; read cd record
;
(defun prompt-for-cd ()
  (make-cd
   ; getting title
   (prompt-read "Title")

   ; getting artist
   (prompt-read "Artist")

   ; parsing numerical rating
   ;    junk-allowed <- t   shows that there is no
   ;                        need to signal about an error in case of wrong string
   ; `or' is here in order to avoid returning NIL from parse-integer function
   (or
    (parse-integer (prompt-read "Rating") :junk-allowed t) 0)
   
   ; getting `is-ripped' flag
   (y-or-n-p "Ripped?")
   )
  )

;=================================================
; add several CDs to a database
;
(defun add-cds ()
  (loop (add-record (prompt-for-cd))
      (if (not (y-or-n-p "Another?")) (return))))

;=================================================
; saving db to file
;
(defun save-db (filename)
  (with-open-file (out filename
                   :direction :output       ; specify output direction
                   :if-exists :supersede    ; specify overwrite-if-exists
                   )
    (with-standard-io-syntax
      (print *db* out))))

;=================================================
; loading database
;
(defun load-db (filename)
  (with-open-file (in filename)
    (with-standard-io-syntax
      (setf *db* (read in)))
    )
  )

;=================================================
;
;

;=================================================
; global path-to-db
;
(setq path-to-db-file "c:/TEMP/temp-db.txt")

;=================================================
; saving db to the certain file in case of user acceptance
;
(defun prompt-save-db ()
 (if (y-or-n-p "save db to the file?")
  (save-db path-to-db-file)
  )
 )

;=================================================
; body to sql-like queries to the database
;
(defun where (&key title artist rating (ripped nil ripped-p))
 #'(lambda (cd)
     (and
      (if title    (equal (getf cd :title)  title)  t)
      (if artist   (equal (getf cd :artist) artist) t)
      (if rating   (equal (getf cd :rating) rating) t)
      (if ripped-p (equal (getf cd :ripped) ripped) t)))
 )

;=================================================
; allows to construct sql-like queries to the database
;
(defun select (selector-fn)
 (remove-if-not selector-fn *db*)
 )

;=================================================
; allows an updating of the requested records
;
(defun update (selector-fn &key title artist rating (ripped nil ripped-p))
 (setf *db*
  (mapcar
   #'(lambda (row)
       (when (funcall selector-fn row)
        (if title    (setf (getf row :title) title))
        (if artist   (setf (getf row :artist) artist))
        (if rating   (setf (getf row :rating) rating))
        (if ripped-p (setf (getf row :ripped) ripped)))
       row) *db*)))

;=================================================
; allows dropping unnecessary records
;
(defun delete-rows (selector-fn)
  (setf *db* (remove-if selector-fn *db*))
  )


;=================================================
; test query
;
(defun test-query ()
 (println "running test query")

 ;(setq query-result (select (where :artist "Bon Jovi" :ripped nil)))
 ;(dump-contents query-result)
 
 ;(update (where :title "Keep the Faith") :rating 9)

 ;
 )

;=================================================
; show all the artists
;
(defun show-all-artists ()
 (dolist (cd *db*)
  (format t "Artist: ~a~%" (getf cd :artist))
  )
 )

;=================================================  
; loading db from the given file
;
(defun prompt-load-db ()
 (if (y-or-n-p "load db from the file?")
  (load-db path-to-db-file))
 )

;=================================================
; defines main app function
;
(defun main ()

 ;
 (println "Here comes the rain")

 ;
 ;(setq A (list 'DFE 'DFA 'DNA))
 ;(setq A (list 'DFA))
 ;(push 'virtual A)
 ;(dolist (i A) (println i))
; (println A)

 

 ; choices array
 ; the structure is:
 ; [text command] - [description] - [function to launch]
 (setq choices
  (list
   (list "add-cds" "Add certain CDs from console" #'add-cds)
   (list (list "l" "load") "Load db from the given file" #'prompt-load-db)
   (list "save" "Save db to the given file" #'prompt-save-db)
   (list (list "d" "dump") "Dump all the db contents to stdout" #'dump-db)

   (list "show-artists" "Show all the artists in db" #'show-all-artists)
   (list "tq" "Do test query" #'test-query)
   )
  ) ; choices

 ; launch command interpreter
 (launch-interpreter-flow choices)

 ; leave the session
 (exit)
 )

;=================================================
; invokes main
;
(main)

